package introduction;

import org.junit.Test;

/**
 * 进程是操作系统调度的最小单位（亦是系统运行程序的基本单位），系统在运行时会为每个进程分配不同的内存区域。
 * <p>
 * 并行(parallel)：指两个或多个事件在同一时刻发生（同时发生）。
 * ----指在同一时刻，有多条指令在多个CPU上同时执行。
 * 并发(concurrency)：指俩个或多个事件在同一时间段内发生。
 * ----即在一段时间内，有多条指令在单个CPU上快速轮换、交替执行，使得在宏观上具有多个进程同时执行的效果。
 * <p>
 * 线程创建方式一：继承Thread类
 * 线程创建方式二：实现Runnable接口
 * 对比两种方式？
 * - 共同点：
 * ① 启动线程，使用的都是Thread类中定义的start()
 * ② 创建的线程对象，都是Thread类或其子类的实例。
 * - 不同点：一个是类的继承，一个是接口的实现
 * 建议使用实现Runnable接口的方式。
 * Runnable方式的好处：
 * - ① 实现的方式，避免类的但继承性的局限性
 * - ② 更适合处理有共享数据的问题。
 * - ③ 实现了代码和数据的分离。
 * 联系：public class Thread implements Runnable{}  (代理模式)
 * <p>
 * 线程中的构造器
 * - public Thread():分配一个新的线程对象
 * - public Thread(String name):分配一个指定名字的新的线程对象
 * - public Thread(Runnable target):指定创建线程的目标对象，它实现了Runnable接口中的run方法
 * - public Thread(Runnable target, String name):分配一个带有指定目标新的线程对象并指定名字。
 * <p>
 * 过时方法：
 * > stop():强行结束一个线程的执行，直接进入死亡状态。不建议使用。
 * > void suspend() / void resume():可能造成死锁，不建议使用。
 */

public class ThreadTest {
    public static void main(String[] args) {
        /*
         * 线程创建方式一：继承Thread类
         * 1.创建一个继承于Thread类的子类
         * 2.重写Thread类的run()
         * 3.创建当前Thread子类的对象
         * 4.通过对象调用start()
         * <p>
         * eg:创建一个分线程1，用于遍历100以内的偶数
         * 再创建一个分线程2，用于遍历100以内的偶数
         */
        PrintNumber printNumber1 = new PrintNumber();
        printNumber1.setName("First");
        printNumber1.start();
        // java.lang.IllegalThreadStateException
        // 不能让已经start()的线程，再次执行start()，否则报异常。
        // printNumber1.start();
        new Thread(() -> {
            for (int i = 0; i < 100; i += 2) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                if (i == 50) {
                    try {
                        // 在线程 A 中，通过线程B调用join()，
                        // 意味着线程A进入阻塞状态，直到线程B执行结束，线程A才结束阻塞状态，继续执行。
                        printNumber1.join();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                if (i > 47 && i < 54) {
                    System.out.println(printNumber1.getName() + " 是否存活？" + printNumber1.isAlive());
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }, "beJoined").start();

        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i += 2)
                    System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }.start();

        /*
         * 线程创建方式二：实现Runnable接口
         * 1.创建一个实现Runnable接口的类
         * 2.实现接口中的run()
         * 3.创建当前实现类的对象
         * 4.将此对象作为参数传递到Thread类的构造器中，创建Thread类的实例
         * 5.Thread类的实例调用start():1.启动线程 2.调用当前线程的run()
         */

        PrintNumberRunnable printNumberRunnable = new PrintNumberRunnable();


        new Thread(printNumberRunnable).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i += 2)
                    System.out.println(Thread.currentThread().getName() + ":" + i);

            }
        }, "Last").start();

        /*
         * The minimum priority that a thread can have.
         */
//        public static final int MIN_PRIORITY = 1;

        /*
         * The default priority that is assigned to a thread.
         */
//        public static final int NORM_PRIORITY = 5;

        /*
         * The maximum priority that a thread can have.
         */
//        public static final int MAX_PRIORITY = 10;
        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

        System.out.println("可用CPU核心数: " + Runtime.getRuntime().availableProcessors());

        for (int i = 0; i < 100; i += 2)
            // getPriority() / setPriority():获取、设置线程得到优先级
            System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);

    }
    @Test
    public void testRunnable() {
        new Thread(new PrintNumberRunnable()).start();
        new Thread(()->{
            for (int i = 0; i < 100; i += 2)
                System.out.println(Thread.currentThread().getName() + ":" + i);
        }).start();
    }
}
